package com.shitou.shitoumember.intercetpor;

import com.shitou.shitoucommons.annotation.AccessLimit;
import com.shitou.shitoucommons.utils.RespBean;
import com.shitou.shitoumember.entity.MemberEntity;
import com.shitou.shitoumember.utils.InterceptorUtils;
import com.shitou.shitoumember.utils.MemberContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

/**
 * @author ShiTou
 * the class {AccessLimitInterceptor} create time is
 * @date 2022年04月24日23:13
 */
@Component
@Slf4j
@SuppressWarnings("all")
public class AccessLimitInterceptor implements HandlerInterceptor {

    @Autowired
    @Qualifier("RedisTemplate")
    private RedisTemplate redisTemplate;

    private InterceptorUtils interceptorUtils = new InterceptorUtils();

    /**
     * 功能描述:  1，进行接口参数校验,
     *          2，路径参数校验，
     *          3，进行AccessLimit注解接口限流校验
     *          4，该方法实际是处理拦截器前调用的一个方法，实质为拦截器HandelInterceptor下的方法的红鞋
     * @param request
     * @param response
     * @param handler 处理的方法
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 如果handler是一个方法，那么进行强转和注解获取
        if ( handler instanceof HandlerMethod){
            HandlerMethod hm = (HandlerMethod) handler;
            // 获得方法上的AccessLimit注解
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
            if ( accessLimit == null ) {
                return true;
            }

            // 获取注解的信息
            int second = accessLimit.second();
            int maxCount = accessLimit.maxCount();
            boolean needLogin = accessLimit.needLogin();
            String key = request.getRequestURL().toString();

            // 获取用户,并存入线程中
            MemberEntity user = interceptorUtils.getUserByCookie(request, response, "memberTicket");
            if ( user == null ) {
                user = interceptorUtils.getUserBySession(request, response, "memberTicket");
            }
            if ( user != null )
                MemberContext.setObject(user);

            if (needLogin) {
                if (user == null) {
                    // 返回错误信息，并拦截当前接口
                    interceptorUtils.render(response, RespBean.error(402, "未登录"));
                    return false;
                }
                // key = 当前访问的路径 + 当前用户的ID
                key += ":" + user.getId();
            }

            Integer count = ((Integer) redisTemplate.opsForValue().get(key));
            if (count == null) {
                // 如果该用户是第一次访问，那么存入redis缓存中
                redisTemplate.opsForValue().set(key, 1, second, TimeUnit.SECONDS);
            } else if (count < maxCount) {
                // 如果当前访问次数小于 在规定时间内访问的最大次数，那么redis中的访问次数自增 1
                redisTemplate.opsForValue().increment(key);
            } else {
                // 当前在一定时间内请求次数已经超过了maxCount 进行接口拦截
                interceptorUtils.render(response, RespBean.error(600, "接口访问次数太频繁"));
                return false;
            }
        }
        return true;
    }

}
